package org.jackysoft.code;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jackysoft.annotations.FormElement;
import org.jackysoft.code.templates.TemplatePosition;
import org.jackysoft.util.IOUtils;
import org.jackysoft.util.StringUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import freemarker.cache.ClassTemplateLoader;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModelIterator;

public final class CodeGenerator {
	private static Log logger = LogFactory.getLog(CodeGenerator.class);
	private static Set<String> views = Sets.newHashSet();
	private Set<String> jsFiles = Sets.newHashSet();
	private Set<String> cssFiles = Sets.newHashSet();
	private Configuration cfg = new Configuration();
	{
		views.add("add.jsp.ftl");
		views.add("list.jsp.ftl");
		views.add("remove.jsp.ftl");
		views.add("update.jsp.ftl");
		views.add("input.jsp.ftl");
		views.add("get.jsp.ftl");
		views.add("gets.jsp.ftl");

		jsFiles.add("add.js.ftl");
		jsFiles.add("list.js.ftl");
		jsFiles.add("remove.js.ftl");
		jsFiles.add("update.js.ftl");
		jsFiles.add("input.js.ftl");
		jsFiles.add("get.js.ftl");

		cssFiles.add("add.css.ftl");
		cssFiles.add("list.css.ftl");
		cssFiles.add("remove.css.ftl");
		cssFiles.add("update.css.ftl");
		cssFiles.add("input.css.ftl");
		cssFiles.add("get.css.ftl");

		ClassTemplateLoader cloader = new ClassTemplateLoader(
				TemplatePosition.class, "");
		cfg.setTemplateLoader(cloader);

		cfg.setObjectWrapper(BeansWrapper.getDefaultInstance());
		cfg.setDefaultEncoding("UTF-8");

	}

	/**
	 * 生成三层代码
	 * 
	 * @author HANTU
	 * @param projectName
	 *            目标eclipse工程名称
	 * @param entityName
	 *            实体名称
	 * @param nosqlable
	 *            是否nosql数据库
	 * @param workspace
	 *            eclipse工作空间绝对路径路径
	 * 
	 *            <pre>
	 *  example use:
	 *  genCode("netfile","HistoryFileItem",false,"f:/java-workspace");
	 * 
	 * </pre>
	 */
	public void generateJavaCode(String projectName, String entityName,
			boolean nosqlable, String workspace,boolean entityExists)
			throws IOException, TemplateException {
		String groupId = "jackysoft";
		
			
		String srcMainJava = "src" + File.separator + "main" + File.separator
				+ "java" + File.separator;

		
		String srcMainJavaPrefix = workspace + File.separator + projectName
				+ File.separator + srcMainJava + "org" + File.separator
				+ groupId + File.separator;
		
		String srcMainResources =  "src" + File.separator + "main" + File.separator
				+ "resources" + File.separator;
		
		String srcMainResourcesPrefix = workspace + File.separator + projectName
				+ File.separator + srcMainResources + "org" + File.separator
				+ groupId + File.separator;
				
	     /* Create a data-model */
		SimpleHash root = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		root.put("nameSpace", "org.jackysoft");
		root.put("entityName", entityName);
		root.put("nosqlable", nosqlable);
		
		if(!entityExists)
			this.generateEntity(srcMainJavaPrefix, entityName, root);
		this.generateController(srcMainJavaPrefix, entityName, root);
		this.generateMapper(srcMainJavaPrefix, entityName, root);
		this.generateMapperXml(srcMainResourcesPrefix, entityName, root);
		this.generateService(srcMainJavaPrefix, entityName, root);
		this.generateServiceImpl(srcMainJavaPrefix, entityName, root);

		

	}

	File generateEntity(String srcMainJavaPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
		
		
		String filePath = srcMainJavaPrefix + "entity" + File.separator + entityName+ ".java";
		String tname = "Entity.ftl";		
		File fi =writeJavaCode(filePath,root,tname);	
		return fi;
	}
	
	File generateController(String srcMainJavaPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
		
		
		String filePath = srcMainJavaPrefix + "controller" + File.separator + entityName	+ "Controller.java";
		String tname = "Controller.ftl";
		File fi =writeJavaCode(filePath,root,tname);	
		return fi;
	}
	
	
	File generateMapperXml(String srcMainResourcesPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
				
		String filePath = srcMainResourcesPrefix+"mapper"+File.separator+entityName+"Mapper.xml";
		String tname = "MapperXml.ftl";			
		File fi = null;
		try {
			fi = writeXmlCode(filePath,root,tname);
		} catch (DocumentException e) {
			logger.error(e);
		}	
		return fi;
				
	
	}
	
	File generateMapper(String srcMainJavaPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
		
		
		String filePath = srcMainJavaPrefix+"mapper"+File.separator+entityName+"Mapper.java";
				
		String tname = "Mapper.ftl";			
		File fi =writeJavaCode(filePath,root,tname);
		return fi;
	}
	
	
	File generateService(String srcMainJavaPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
		
		
		String filePath = srcMainJavaPrefix + "service" + File.separator	+ entityName + "Service.java";
		
		String tname = "Service.ftl";			
		File fi =writeJavaCode(filePath,root,tname);
		return fi;
	}
	
	
		File generateServiceImpl(String srcMainJavaPrefix,String entityName,SimpleHash root) throws IOException, TemplateException{
		
		String filePath = srcMainJavaPrefix + "service" + File.separator+ "impl" + File.separator + entityName + "ServiceImpl.java";
		String tname = "ServiceImpl.ftl";			
		File fi =writeJavaCode(filePath,root,tname);
		return fi;
	}
	
		
		File writeJavaCode(String path,SimpleHash root,String tempName) throws IOException, TemplateException{
			
	 		File fi = new File(path);
	 		if(fi.exists())return fi;
	 		IOUtils.makeDirectories(new File(path).getParentFile());
				
	 		String tname = tempName;
			Template temp  = cfg.getTemplate(tname);
			temp.setEncoding("UTF-8");
			Writer fw  = new FileWriter(fi);
			temp.process(root, fw);
			fw.close();
			logger.info("文件 " + path + "  已生成  ");
			return fi;
			
	 	}
		
		
		File writeXmlCode(String path,SimpleHash root,String tempName) throws IOException, TemplateException, DocumentException{
			
	 		File file = new File(path);
	 		SAXReader readerolder = new SAXReader();
	 		
	 		Document documentold = readerolder.read(file);
	 		
	 		Element rootBodyold =  documentold.getRootElement(); 
	 		
	 		Iterator olderItr = rootBodyold.elementIterator();
	 		
	 		
	 		//生成新的xml
	 		IOUtils.makeDirectories(new File(path).getParentFile());				
	 		String tname = tempName;
			Template temp  = cfg.getTemplate(tname);
			temp.setEncoding("UTF-8");
			StringWriter fw  = new StringWriter();
			temp.process(root, fw);
			fw.close();
			
			StringReader srsource=  new StringReader(fw.toString());
			
			SAXReader readernewer = new SAXReader();
			Document documentnewer = readernewer.read(srsource);
			Element rootnewer = documentnewer.getRootElement();
			Iterator itr = rootnewer.elementIterator();
			List<Element> newlist = Lists.newArrayList();
			for(;itr.hasNext();){
				Element newel = (Element) itr.next();
			    newlist.add(newel);
			}
			
			
			for(;olderItr.hasNext();){
				Element oldel = (Element) olderItr.next();
				
				for(int i=0;i<newlist.size();i++){
					Element newel =  newlist.get(i);
					if(newel.attributeValue("id").equals(oldel.attributeValue("id"))){
						continue;
					}
					
					Element newcopy = newel.createCopy();
					rootBodyold.add(newcopy);
				}
				
				
			}	
		
			
			XMLWriter xmlWriter = new XMLWriter(new FileWriter(file));  
            xmlWriter.write(documentold);  
            xmlWriter.flush();  
            xmlWriter.close();	         
			
			logger.info("文件 " + path + "  已生成  ");
			return file;
			
	 	}
		
	/**
	 * <ul>
	 * <li>start 显示添加实体的界面
	 * <li>list 显示列表页面的界面
	 * <li>get 显示编辑页面的界面
	 * </ul>
	 * 
	 * @param cls
	 *            实体类元数据
	 * @param workspace
	 *            eclipse工作空间
	 * @param viewProjectName
	 *            视图层工程名
	 * @param webapp
	 *            web工程目录
	 * @throws IOException 
	 * @throws TemplateException 
	 * 
	 * */
 	
		
		
	public void generateViews(Class<?> cls, String workspace, String project)
			throws IOException, TemplateException {
		SimpleHash root = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		String entityName = cls.getSimpleName();
		root.put("entityName", entityName);
		Method[] gets = cls.getMethods();
		SimpleHash fields = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
		for (Method getm : gets) {
			if (getm.isAnnotationPresent(FormElement.class)) {
				String name = getm.getName();
				String indexName = StringUtils
						.lowerFirstChar(name.substring(3));
				name = "bean." + indexName;
				FormElement fe = getm.getAnnotation(FormElement.class);
				SimpleHash fs = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
				fs.put("id", fe.id());
				fs.put("name", name);
				fs.put("indexName", indexName);
				fs.put("type", fe.type().toString());
				fs.put("label", fe.label());
				fs.put("listable", fe.listable() + "");
				fs.put("value", fe.value());
				fields.put(indexName, fs);

			}
		}

		root.put("formfields", fields);
		String webapp = "src"+File.separator+"main"+File.separator+"webapp";
		String srcBase = workspace + File.separator + project + File.separator
				+ webapp + File.separator;
		String viewBase = srcBase + "spring" + File.separator
				+ entityName.toLowerCase();
		String jsBase = srcBase + "scripts" + File.separator + entityName.toLowerCase();

		String cssBase = srcBase + "css" + File.separator + entityName.toLowerCase();

		logger.info("生成表示层... [START]");

		// 生成各种action页面
		writeFiles(views, viewBase, root, cfg, entityName);
		// 生成js
		writeFiles(jsFiles, jsBase, root, cfg, null);
		// 生成css
		writeFiles(cssFiles, cssBase, root, cfg, null);
		logger.info("生成表示层... [OK]");

	}

	void writeSuccess(String actionName, String template, String base,
			SimpleHash root, Configuration cfg) throws IOException,
			TemplateException {
		File vpath = new File(base);
		if (!vpath.exists()) {
			vpath.mkdir();
		}
		String name = actionName + ".jsp";
		File vf = new File(vpath, name);
		Template vtemp = cfg.getTemplate(template);
		vtemp.setEncoding("UTF-8");
		vtemp.process(root, new FileWriter(vf));
		logger.info("文件 :" + vf.getAbsolutePath() + " 已经生成!");

	}

	void writeFiles(Set<String> templates, String base, SimpleHash root,
			Configuration cfg, String entityName) throws IOException,
			TemplateException {
		File vpath = new File(base);
		IOUtils.makeDirectory(vpath);
		if (entityName != null) {
			
			writeSuccess(entityName.toLowerCase(), "success.jsp.ftl", base, root, cfg);
			
		}
		for (String view : templates) {
			int linx = view.lastIndexOf('.');
			String name = view.substring(0, linx);
			File vf = new File(vpath, name);
			Template vtemp = cfg.getTemplate(view);
			vtemp.setEncoding("UTF-8");
			vtemp.process(root, new FileWriter(vf));
			logger.info("文件 :" + vf.getAbsolutePath() + " 已经生成!");
		}
	}

}
